/****************************************************************************
 * @file    : lin_eeprom.c
 * @author  : Nonosns MCU Team
 * @version : V2.0
 * @Date    : 2023/4/7
 * @brief   : lin stack file
 * @note
 * Copyright (C) 2023 Novosense All rights reserved.
 ****************************************************************************/

/*******************************************************************************
 * Include files
 *******************************************************************************/
#include <string.h>
#include "include.h"
#include "lin_define.h"
#include "lin_config.h"
#include "lin_api.h"
#include "lin_api_appl.h"
#include "lin_identification_and_configuration.h"
#include "lin_eeprom.h"

extern l_u8 g_nad; // slave node address

#ifdef L_UDS_BOOTLOADER_UPDATE
static const BootInfo g_stBootInfo = {
    .infoDataLen = 8u,
    .requestEnterBootloader = 0x5Au,
    .downloadAPPSuccessful = 0xA5u,
    .infoStartAddr = EEPROMMEM_BASE + 8 * BOOT_INFO_EEPROM_PAGE,
    .requestEnterBootloaderAddr = EEPROMMEM_BASE + 8 * BOOT_INFO_EEPROM_PAGE + 1,
    .downloadAppSuccessfulAddr = EEPROMMEM_BASE + 8 * BOOT_INFO_EEPROM_PAGE};
#endif

/************************************************************
 * @brief: EEPROM erase pages 1个page8个字节，擦除操作只能按page执行
 * @param <uint32_t> addr
 * @param <uint8_t> pages
 * @author: Nonosns MCU Team
 * @return <None>
 ************************************************************/
static void EEPROM_Erase(uint32_t addr, uint8_t page)
{
    /* Check  Status of SBC */
    while (EEPROM_CTRL->FSR_b.BUSY != 0x00)
        ;
    EEPROM_CTRL->ECR_b.WLA = 1;
    *((uint32_t *)(addr + 8 * page)) = 0x00;
    *((uint32_t *)(addr + 8 * page + 4)) = 0x00;
    EEPROM_CTRL->ECR_b.WLA = 0;
    EEPROM_CTRL->ECR_b.ERA = 1;
    /* Check  Status of ERA */
    while (EEPROM_CTRL->ECR_b.ERA != 0x00)
        ;
}

/************************************************************
 * @brief: EEPROM write page
 * @param <uint32_t> addr
 * @param <uint8_t> page
 * @param <uint32_t> *data
 * @author: Nonosns MCU Team
 * @return <None>
 ************************************************************/
static void EEPROM_Program(uint32_t addr, uint8_t page, uint32_t *data)
{
    /* Check  Status of SBC */
    while (EEPROM_CTRL->FSR_b.BUSY != 0x00)
        ;
    /* data changed */
    *((uint32_t *)(addr + 8 * page)) = data[0];
    *((uint32_t *)(addr + 8 * page + 4)) = data[1];
    /* Check  Status of SBC */
    while (EEPROM_CTRL->FSR_b.BUSY != 0x00)
        ;
}

#ifdef L_UDS_BOOTLOADER_UPDATE
/************************************************************
 * @brief: set download app successful
 * @author: Nonosns MCU Team
 * @return <None>
 ************************************************************/
void SetRequestEnterBootloader(void)
{
    uint32_t bootInfo[2] = {0x00, 0x00};
    bootInfo[0] = (uint16_t)g_stBootInfo.requestEnterBootloader << 8;
    bootInfo[1] = ((uint32_t)UPDATE_FIRMWARE_FIX_CRC_CODE) << 16;
    /* unlock sysctrl register */
    SYSCTRL->LKKEYR = SYSCTRL_UNLOCK_KEY;
    /* ENABLE AHB EEPROM CLK */
    SYSCTRL->AHBCGR_b.EPRCEN = 1;
    /* unlock EEPROM */
    EEPROM_CTRL->OKR = EEPROM_UNLOCK_KEY1;
    EEPROM_CTRL->OKR = EEPROM_UNLOCK_KEY2;
    /* config EEPROM */
    EEPROM_CTRL->ECR_b.SBIE = 1;
    EEPROM_CTRL->ECR_b.DBIE = 1;

    EEPROM_Erase(EEPROMMEM_BASE, BOOT_INFO_EEPROM_PAGE);
    EEPROM_Program(EEPROMMEM_BASE, BOOT_INFO_EEPROM_PAGE, bootInfo);
}
#endif

#ifdef L_NAD_EEPROM
/******************************************************************************
 * @brief: Read NAD from EEPROM (if valid) and store ist to g_nad.
 * @author: Nonosns MCU Team
 * @return <None>
 *******************************************************************************/
void ReadNADFromEEPROM(void)
{
    uint32_t eepromCode = *((uint32_t *)(EEPROMMEM_BASE + 8 * EEPROM_NAD_PAGE));
    if ((eepromCode >> 16) == NAD_VALID_PATTERN)
    {
        g_nad = eepromCode & 0xFF;
    }
    else
    {
        g_nad = INITIAL_NAD;
    }
}

/******************************************************************************
 * @brief: Write NAD and NAD pattern to EEPROM
 * @author: Nonosns MCU Team
 * @return <None>
 *******************************************************************************/
void WriteNADToEEPROM(void)
{
    uint32_t eepromCode[2] = {0, 0};
    eepromCode[0] = (NAD_VALID_PATTERN << 16) + g_nad;
    /* unlock sysctrl register */
    SYSCTRL->LKKEYR = SYSCTRL_UNLOCK_KEY;
    /* ENABLE AHB EEPROM CLK */
    SYSCTRL->AHBCGR_b.EPRCEN = 1;
    /* unlock EEPROM */
    EEPROM_CTRL->OKR = EEPROM_UNLOCK_KEY1;
    EEPROM_CTRL->OKR = EEPROM_UNLOCK_KEY2;

    /* config EEPROM */
    EEPROM_CTRL->ECR_b.SBIE = 1;
    EEPROM_CTRL->ECR_b.DBIE = 1;

    EEPROM_Erase(EEPROMMEM_BASE, EEPROM_NAD_PAGE); // erase page
    /* this Eeprom operation will pause CPU ,then the programming process cannot be disturbed */
    EEPROM_Program(EEPROMMEM_BASE, EEPROM_NAD_PAGE, eepromCode); // write page
}

#endif

#ifdef L_PID_EEPROM
/******************************************************************************
 * @brief: Read PID from EEPROM (if valid) and store ist to g_nad.
 * @author: Nonosns MCU Team
 * @return <None>
 *******************************************************************************/
void ReadPIDFromEEPROM(uint8_t *PIDTable, uint8_t len)
{
    uint64_t eepromCode = *((uint64_t *)(EEPROMMEM_BASE + 8 * EEPROM_PID_PAGE));
    uint8_t lenCode = eepromCode & 0xFF;
    uint8_t lenInvertCode = (eepromCode >> 8) & 0xFF;
    uint8_t invertLen = ~len;
    if (len <= 6 && lenCode == len && lenInvertCode == invertLen)
    {
        uint8_t *pidAddr = (uint8_t *)&eepromCode + 2;
        for (int i = 0; i < len; i++)
        {
            PIDTable[i] = pidAddr[i];
        }
    }
}

/******************************************************************************
 * @brief: Write PID , PID len code and len invert code to EEPROM
 * @author: Nonosns MCU Team
 * @return <None>
 *******************************************************************************/
void WritePIDToEEPROM(uint8_t *PIDTable, uint8_t len)
{
    uint32_t eepromCode[2] = {0, 0};
    if (len > 6)
    {
        return;
    }
    eepromCode[0] = len + ((uint16_t)(~len) << 8);
    uint8_t *pidAddr = (uint8_t *)&eepromCode + 2;
    for (int i = 0; i < len; i++)
    {
        pidAddr[i] = PIDTable[i];
    }
    /* unlock sysctrl register */
    SYSCTRL->LKKEYR = SYSCTRL_UNLOCK_KEY;
    /* ENABLE AHB EEPROM CLK */
    SYSCTRL->AHBCGR_b.EPRCEN = 1;
    /* unlock EEPROM */
    EEPROM_CTRL->OKR = EEPROM_UNLOCK_KEY1;
    EEPROM_CTRL->OKR = EEPROM_UNLOCK_KEY2;

    /* config EEPROM */
    EEPROM_CTRL->ECR_b.SBIE = 1;
    EEPROM_CTRL->ECR_b.DBIE = 1;

    EEPROM_Erase(EEPROMMEM_BASE, EEPROM_PID_PAGE); // erase page
    /* this Eeprom operation will pause CPU ,then the programming process cannot be disturbed */
    EEPROM_Program(EEPROMMEM_BASE, EEPROM_PID_PAGE, eepromCode); // write page
}
#endif
